#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Parser.h"
void eat(int token);
void skip(int sync[]);
void eatOrSkipTo(int token, int sync[]);
void eatOrSkipTo(int token, int sync[], int error);
void classDecl(node_ClassList *ClassList);
void otherClass(node_ClassList *ClassList);
void extendsExist(node_ClassList *ClassList);
node_LocalDecl* localDecl();
void varDecl(node_VarList *Var);
void idList(node_VarList *Var, node_Type *Type);
node_LocalDecl* idList2(node_VarList *Var, node_Type *Type);
void methodDecl(node_MethodList *MethodList);
node_Type* isStatic();
node_VarList* formalList(node_VarList *ParamList);
node_LocalDecl* formalRest(node_VarList *ParamList);
node_Type* type(node_Type *Type);
int primitiveType();
bool haveBracket();
node_StmtList* stmtList(node_StmtList *StmtList);
node_Stmt* stmt();
node_StmtList* stmt1();
node_Stmt* stmt2();
node_Stmt* declORExpr(node_Type *Type, node_Name *Var);
node_Stmt* isAssign(node_Name *Name);
node_StmtList* if2();
node_Stmt* printRead();
node_Exp* assign();
node_Exp* New();
node_ExpList* exprList();
node_ExpList* exprListTail();
node_ExpList* haveExprListTail();
node_Name* name();
node_Name* name1(node_Name *Name);
node_Name* name2(node_Name* Name);
node_Name* name3(node_Name* Name);
node_ExpList* isMethod();
node_Exp* expr();
node_Exp* opOR();
node_Exp* opOR1(node_Exp *exp);
node_Exp* opAND();
node_Exp* opAND1(node_Exp* exp);
node_Exp* opEq();
node_Exp* opEq1(node_Exp* exp);
node_Exp* opRel();
node_Exp* opRel1(node_Exp* exp);
node_Exp* opBinOR();
node_Exp* opBinOR1(node_Exp* exp);
node_Exp* opBinAND();
node_Exp* opBinAND1(node_Exp *exp);
node_Exp* unOp();
node_Exp* varConst();
node_Name* haveLength(node_Name* Name);

static int lookAhead; //Token de lookahead.
static bool isArray, ismethod = false;
static node_Program *Program;
static VisitorASTprint *ASTprint;

static int classDecl_sync1[] = {ID,EOF};
static int classDecl_sync2[] = {EXTENDS,LBRACE,EOF};
static int classDecl_sync3[] = {PUBLIC,ID,BOOLEAN,INT,FLOAT,RBRACE,EOF};
static int classDecl_sync4[] = {CLASS, EOF};
static int extendsExist_sync1[] = {ID,EOF};
static int extendsExist_sync2[] = {LBRACE,EOF};
static int varDecl_sync[] = {PUBLIC,ID,BOOLEAN, INTNUM, REALNUM,EOF};
static int stmt_sync1[] = {ORLOGIC,ANDLOGIC,EQUAL,NOTEQUAL,LESST,GREATERT,LESSEQ,GREATEREQ,PLUS,SUB,ORBIT,MULT,DIV,MOD,NOT,INTNUM,REALNUM,LITERAL,FALSE,TRUE,LPARENT,ID,THIS,EOF};
static int stmt_sync2[] = {IF,WHILE,SYSTEM,RETURN,ID,THIS,BOOLEAN,INT,FLOAT,LBRACE,EOF};
static int stmt_sync3[] = {OUT, IN, EOF};
static int stmt_sync4[] = {IF, WHILE, SYSTEM, RETURN, ID, THIS, BOOLEAN, INTNUM, REALNUM, RBRACE, EOF};
static int idList_sync[] = {COMMA,SEMICOLON,EOF};
static int idList2_sync1[] = {ID,EOF};
static int idList2_sync2[] = {COMMA,SEMICOLON,EOF};
static int methodDecl_sync1[] = {LPARENT,STATIC,ID,INT,BOOLEAN,FLOAT,EOF};
static int methodDecl_sync2[] = {ID,LPARENT,EOF};
static int methodDecl_sync3[] = {ID,BOOLEAN,INT,FLOAT,RPARENT,LBRACE,EOF};
static int methodDecl_sync4[] = {LBRACE,EOF};
static int methodDecl_sync5[] = {IF,WHILE,SYSTEM,RETURN,ID,THIS,BOOLEAN,INT,FLOAT,RBRACE,EOF};
static int methodDecl_sync6[] = {PUBLIC,ID,BOOLEAN,INT,FLOAT,RBRACE,EOF};
static int isStatic_sync[] = {ID,LPARENT,EOF};
static int formalList_sync[] = {RPARENT,EOF};
static int formalRest_sync1[] = {ID,BOOLEAN,INT,FLOAT,EOF};
static int formalRest_sync2[] = {COMMA,ID,BOOLEAN,INT,FLOAT,EOF};
static int type_sync[] = {ID,LPARENT,EOF};
static int haveBracket_sync[] = {ID,EOF};
static int stmt2_sync1[] = {SEMICOLON,EOF};
static int stmt2_sync2[] = {IF,WHILE,SYSTEM,RETURN,INT,FLOAT,BOOLEAN,ID,THIS,RBRACE,EOF};
static int stmt2_sync3[] = {ID,LBRACKET,DOT,SEMICOLON,ASSIGN,EOF};
static int isMethod_sync1[] = {NOTEQUAL, PLUS, SUB, INTNUM, REALNUM, LITERAL, TRUE, FALSE, ID, THIS, RPARENT, EOF};
static int isMethod_sync2[] = {MULT, DIV, MOD, ANDBIT, PLUS, SUB, ORBIT, LESST, LESSEQ, GREATERT, GREATEREQ, EQUAL, NOTEQUAL, ANDLOGIC, ORLOGIC, LBRACKET, DOT, ASSIGN, COMMA, SEMICOLON, RBRACKET, RPARENT, EOF};
static int isAssign_sync[] = {IF, WHILE, SYSTEM, RETURN, ID, THIS, BOOLEAN, INTNUM, REALNUM, RBRACE, EOF};
static int declORExpr_sync1[] = {IF, WHILE, SYSTEM, RETURN, ID, THIS, BOOLEAN, INTNUM, REALNUM, RBRACE, EOF};
static int declORExpr_sync2[] = {NOT, PLUS, SUB, INTNUM, REALNUM, LITERAL, TRUE, FALSE, ID, THIS, RPARENT, EOF};
static int declORExpr_sync3[] = {DOT, SEMICOLON, EOF};
static int printRead_sync[] = {SEMICOLON, EOF};
static int stmt1_sync[] = {ELSE,IF,WHILE,SYSTEM,RETURN,ID,THIS,BOOLEAN,INT,FLOAT,PUBLIC,RBRACE,EOF};
static int New_sync1[] = {ORLOGIC,ANDLOGIC,EQUAL,NOTEQUAL,LESST,GREATERT,LESSEQ,GREATEREQ,PLUS,SUB,ORBIT,MULT,DIV,MOD,NOT,INTNUM,REALNUM,LITERAL,FALSE,TRUE,LPARENT,ID,THIS,EOF};
static int New_sync2[] = {SEMICOLON,EOF};
static int New_sync3[] = {RPARENT,SEMICOLON,EOF};
static int assign_sync[] = {SEMICOLON,EOF};
static int name_sync[] = {RPARENT,EOF};
static int name1_sync[] = {DOT, MULT, DIV, MOD, ANDBIT, PLUS, SUB, ORBIT, LESST, LESSEQ, GREATERT, GREATEREQ, EQUAL, NOTEQUAL, ANDLOGIC, ORLOGIC, SEMICOLON, ASSIGN, COMMA, RBRACKET, RPARENT, EOF};
static int name3_sync2[] = {NOT, PLUS, SUB, INTNUM, REALNUM, LITERAL, TRUE, FALSE, ID, THIS, EOF};
static int name3_sync3[] = {DOT, SEMICOLON, EOF};
static int unOp_sync[] = {MULT,DIV,MOD,ANDBIT,ORBIT,PLUS,SUB,LESST,LESSEQ,GREATEREQ,GREATERT,EQUAL,NOTEQUAL,ANDLOGIC,ORLOGIC,SEMICOLON,COMMA,RBRACKET,RPARENT,EOF};
static int haveLength_sync[] = {MULT, DIV, MOD, ANDBIT, PLUS, SUB, ORBIT, LESST, LESSEQ, GREATERT, GREATEREQ, EQUAL, NOTEQUAL, ANDLOGIC, ORLOGIC, SEMICOLON, COMMA, RBRACKET, EOF};
static int varConst_sync[] = {MULT,DIV,MOD,ANDBIT,ORBIT,PLUS,SUB,LESST,LESSEQ,GREATEREQ,GREATERT,EQUAL,NOTEQUAL,ANDLOGIC,ORLOGIC,SEMICOLON,COMMA,RBRACKET,RPARENT,EOF};

void eat(int token) {
/*
    fprintf(stdout, "MATCH:");
    printToken(token);
    fprintf(stdout, "\n");
*/
    lookAhead = nextToken();
}

void skip(int sync[]) {
    while (lookAhead != EOF) {
        for (int i=0; sync[i] != EOF; i++) {
            if (lookAhead == sync[i])
                return;
        }
        lookAhead = nextToken();
    }
}

void eatOrSkipTo(int token, int sync[]) {
    if (token == lookAhead)
        eat(token);
    else {
        ErrorMessage(getLine(), lookAhead, token);
        skip(sync);
    }
}

void eatOrSkipTo(int token, int sync[], int error) {
    if (token == lookAhead)
        eat(token);
    else {
        ErrorMessage(error, getLine());
        skip(sync);
    }
}

void parserConstructor() {
/*
    fprintf(stdout, "+==========================================================================+\n");
    fprintf(stdout, "|                                 PARSING                                  |\n");
    fprintf(stdout, "+==========================================================================+\n\n");
*/
    lookAhead = nextToken();
}

void parserDestructor(){
    delete Program;
    delete ASTprint;
}

node_Program* program(){
 //   --> <ClassDecl><OtherClass>

/*
    fprintf(stdout, "<Program>\n");
*/

    node_ClassList *ClassList = new node_ClassList(NULL, NULL, NULL, NULL, 0);
    classDecl(ClassList);
    otherClass(ClassList);
    return new node_Program(ClassList);
}

void otherClass(node_ClassList *ClassList){
 //   --> <ClassDecl><OtherClass>			|
//						epsilon

/*
    fprintf(stdout, "<OtherClass>\n");
*/

    if(lookAhead != EOF){
        node_ClassList *OtherClass = new node_ClassList(NULL, NULL, NULL, NULL, 0);
        classDecl(OtherClass);
        ClassList->nextClass = OtherClass;
        otherClass(OtherClass);
    }else return;
}

void classDecl(node_ClassList *ClassList){
//    --> class ID <ExtendsExist> { <LocalDecl> }

/*
    fprintf(stdout, "<ClassDecl>\n");
*/

    ClassList->line = getLine();
    eatOrSkipTo(CLASS, classDecl_sync1);
    if(lookAhead == ID){
        ClassList->className = (char*)malloc((getLexemeLength()+1)*sizeof(char));
        strcpy(ClassList->className, getLexeme());
        eat(ID);
    }else{
        ErrorMessage(getLine(), lookAhead, ID);
        skip(classDecl_sync2);
    }
    extendsExist(ClassList);
    eatOrSkipTo(LBRACE, classDecl_sync3);
    ClassList->firstDecl = localDecl();
    eatOrSkipTo(RBRACE, classDecl_sync4, RBRACE_CLASS_EXPECTED);
    return;
}

void extendsExist(node_ClassList *ClassList){
//    --> extends ID              		|
//				epsilon

/*
    fprintf(stdout, "<ExtendsExist>\n");
*/


    switch(lookAhead){
        case EXTENDS: eatOrSkipTo(EXTENDS, extendsExist_sync1);
                      if(lookAhead == ID){
                          ClassList->extendedClassName = (char*)malloc((getLexemeLength()+1)*sizeof(char));
                          strcpy(ClassList->extendedClassName, getLexeme());
                          eat(ID);
                      }else{
                            skip(extendsExist_sync2);
                            ErrorMessage(getLine(), lookAhead, ID);
                      }
                      break;
        case ID: ErrorMessage(MISSED_EXTENDS, getLine());
                 ClassList->extendedClassName = (char*)malloc((getLexemeLength()+1)*sizeof(char));
                 strcpy(ClassList->extendedClassName, getLexeme());
                 eat(ID);
                 break;
    }
    return;
}

node_LocalDecl* localDecl(){
/*
                    --> <MethodDecl> <LocalDecl> 		|
						<VarDecl> <LocalDecl>		    |
						epsilon
*/

/*
    fprintf(stdout, "<LocalDecl>\n");
*/

    node_MethodList *MethodList = new node_MethodList(NULL, NULL, NULL, NULL, NULL, NULL, 0);
    node_VarList *VarList = new node_VarList(NULL, NULL);
    node_VarList *VarList2 = new node_VarList(NULL, NULL);
    node_VarList *aux;
    switch(lookAhead){
        case PUBLIC: MethodList->line = getLine();
                     methodDecl(MethodList);
                     MethodList->nextDecl = localDecl();
                     return (node_LocalDecl*)MethodList;

        case INT:
        case FLOAT:
        case BOOLEAN:
        case ID:    varDecl(VarList);
                    VarList2 = (node_VarList*)localDecl();
                    aux = (node_VarList*)VarList;
                    while(aux->nextDecl != NULL) aux = (node_VarList*)aux->nextDecl;
                    aux->nextDecl = VarList2;
                    return (node_LocalDecl*)VarList;

        default: return NULL;
    }
}

void varDecl(node_VarList *Var){
/*
    --> <Type> <IdList>;
*/

    node_Type *Type = new node_Type(NULL, 0, false);
    node_VarList *VarList = new node_VarList(NULL, NULL);

/*
    fprintf(stdout, "<VarDecl>\n");
*/

    switch(lookAhead){
        case INT:
        case FLOAT:
        case BOOLEAN:
        case ID:
                 Type = type(Type);
                 idList(VarList, Type);
                 Var->VarName = VarList->VarName;
                 Var->nextDecl = VarList->nextDecl;
                 eatOrSkipTo(SEMICOLON, varDecl_sync);
                 break;
    }
    return;
}

void idList(node_VarList *Var, node_Type *Type){
//    --> ID<IdList2>

/*
    fprintf(stdout, "<IdList>\n");
*/

    node_NameDecl *Variable = new node_NameDecl(Type, NULL, 0);
    node_VarList *NextVar = new node_VarList(NULL, NULL);

    if(lookAhead == ID){
        Variable->varName = (char*)malloc((getLexemeLength()+1)*sizeof(char));
        strcpy(Variable->varName, getLexeme());
        Var->VarName = Variable;
        Variable->line = getLine();
        eat(ID);
    }else{
        ErrorMessage(getLine(), lookAhead, ID);
        skip(idList_sync);
    }
    Var->nextDecl = idList2(NextVar, Type);
    return;
}

node_LocalDecl* idList2(node_VarList *Var, node_Type *Type){
/*
    --> ,ID<IdList2>					|
						epsilon
*/

/*
    fprintf(stdout, "<IdList2>\n");
*/

    node_NameDecl *Variable = new node_NameDecl(Type, NULL, 0);
    node_VarList *NextVar = new node_VarList(NULL, NULL);

    switch(lookAhead){
        case COMMA:
        case ID:
            eatOrSkipTo(COMMA, idList2_sync1);
            if(lookAhead == ID){
                Variable->varName = (char*)malloc((getLexemeLength()+1)*sizeof(char));
                Variable->line = getLine();
                strcpy(Variable->varName, getLexeme());
                Var->VarName = Variable;
                eat(ID);
            }else{
                ErrorMessage(getLine(), lookAhead, ID);
                skip(idList2_sync2);
            }
            Var->nextDecl = idList2(NextVar, Type);
            return Var;
    }
    return NULL;
}

void methodDecl(node_MethodList *MethodList){
//   --> public <IsStatic> ID ( <FormalList> ) { <StmtList> }

/*
    fprintf(stdout, "<MethodDecl>\n");
*/


    eatOrSkipTo(PUBLIC, methodDecl_sync1);
    MethodList->returnType = isStatic();
    if(lookAhead == ID){
        MethodList->methodName = (char*)malloc((getLexemeLength()+1)*sizeof(char));
        strcpy(MethodList->methodName, getLexeme());
        eat(ID);
    }else{
        ErrorMessage(getLine(), lookAhead, ID);
        skip(methodDecl_sync2);
    }
    eatOrSkipTo(LPARENT, methodDecl_sync3, LPARENT_EXPECTED);
    node_VarList *ParamList = new node_VarList(NULL, NULL);
    MethodList->paramList = formalList(ParamList);
    eatOrSkipTo(RPARENT, methodDecl_sync4, RPARENT_EXPECTED);
    eatOrSkipTo(LBRACE, methodDecl_sync5);
    node_StmtList *MethodStmtList = new node_StmtList(NULL, NULL);
    MethodList->stmtList = stmtList(MethodStmtList);
    eatOrSkipTo(RBRACE, methodDecl_sync6, RBRACE_METHOD_EXPECTED);
    return;
}

node_Type* isStatic(){
/*
    --> static void                 	|
						<Type>
*/

/*
    fprintf(stdout, "<IsStatic>\n");
*/

    node_Type *Type = new node_Type(NULL, 0, false);

    switch(lookAhead){
        case STATIC:
            eat(STATIC);
            eatOrSkipTo(VOID, isStatic_sync);
            Type->primitiveType = VOID;
            break;

        case INT:
        case FLOAT:
        case BOOLEAN:
        case ID:
            Type = type(Type);
            break;

        default:
            ErrorMessage(STATIC_OR_TYPE_EXPECTED, getLine());
            skip(isStatic_sync);
            break;
    }
    return Type;
}

node_VarList* formalList(node_VarList *ParamList){
/*
    --> <Type> ID <FormalRest>			|
						epsilon
*/

/*
    fprintf(stdout, "<FormalList>\n");
*/

    node_Type *Type = new node_Type(NULL, 0, false);
    node_VarList *NextParam = new node_VarList(NULL, NULL);

    switch(lookAhead){
        case INT:
        case FLOAT:
        case BOOLEAN:
        case ID:
        case COMMA:
                Type = type(Type);
                if(lookAhead == ID){
                    node_NameDecl *Param = new node_NameDecl(Type, NULL, 0);
                    Param->line = getLine();
                    Param->varName = (char*)malloc((getLexemeLength()+1)*sizeof(char));
                    strcpy(Param->varName, getLexeme());
                    ParamList->VarName = Param;
                    eat(ID);
                }else{
                    ErrorMessage(getLine(), lookAhead, ID);
                    skip(formalList_sync);
                }
                ParamList->nextDecl = formalRest(NextParam);
                return ParamList;
    }
    return NULL;
}

node_LocalDecl* formalRest(node_VarList *ParamList){
/*
    --> ,<Type> ID <FormalRest>			|
						epsilon
*/

/*
    fprintf(stdout, "<FormalRest>\n");
*/

    node_Type *Type = new node_Type(NULL, 0, false);
    node_VarList *NextParam = new node_VarList(NULL, NULL);

    switch(lookAhead){
        case COMMA:
        case INT:
        case FLOAT:
        case BOOLEAN:
        case ID:
            eatOrSkipTo(COMMA, formalRest_sync1);
            Type = type(Type);
            if(lookAhead == ID){
                node_NameDecl *Param = new node_NameDecl(Type, NULL, 0);
                Param->line = getLine();
                Param->varName = (char*)malloc((getLexemeLength()+1)*sizeof(char));
                strcpy(Param->varName, getLexeme());
                ParamList->VarName = Param;
                eat(ID);
            }else{
                ErrorMessage(getLine(), lookAhead, ID);
                skip(formalRest_sync2);
            }
            ParamList->nextDecl = formalRest(NextParam);
            return ParamList;
    }
    return NULL;
}

node_Type* type(node_Type *Type){
/*
    --> ID								|
						<PrimitiveType>
*/

/*
    fprintf(stdout, "<Type>\n");
*/

    switch(lookAhead){
        case ID:
                Type->className = (char*)malloc((getLexemeLength()+1)*sizeof(char));
                strcpy(Type->className, getLexeme());
                eat(ID);
                break;

        case INT:
        case FLOAT:
        case BOOLEAN:
                Type->primitiveType = primitiveType();
                Type->isArray = isArray;
                break;

        default:
                ErrorMessage(TYPE_EXPECTED, getLine());
                skip(type_sync);
                break;
    }
    if(Type->className == NULL && Type->primitiveType == 0) return NULL;
    else return (node_Type*)Type;
}

int primitiveType(){
/*
    -->	boolean							|
						int<HaveBracket>				|
						float<HaveBracket>
*/

/*
    fprintf(stdout, "<PrimitiveType>\n");
*/

    switch(lookAhead){
        case BOOLEAN:
            eat(BOOLEAN);
            return LOGICAL;

        case INT:
            eat(INT);
            isArray = haveBracket();
            if(isArray) return INTVECTOR;
            return INTNUM;

        case FLOAT:
            eat(FLOAT);
            isArray = haveBracket();
            if(isArray) return REALVECTOR;
            return REALNUM;
    }
    return false;
}

bool haveBracket(){
/*
    --> []								|
						epsilon
*/

/*
    fprintf(stdout, "<HaveBracket>\n");
*/

    switch(lookAhead){
        case LBRACKET: eat(LBRACKET);
                       eatOrSkipTo(RBRACKET, haveBracket_sync);
                       return true;

        case RBRACKET: eat(RBRACKET);
                       ErrorMessage(LBRACKET_EXPECTED, getLine()) ;
                       return true;
    }
    return false;;
}

node_StmtList* stmtList(node_StmtList *StmtList){
/*
    --> <Stmt> <StmtList>				|
						epsilon
*/

/*
    fprintf(stdout, "<StmtList>\n");
*/

    node_StmtList *NextStmt = new node_StmtList(NULL, NULL);
    switch(lookAhead){
        case IF:
        case WHILE:
        case SYSTEM:
        case RETURN:
        case THIS:
        case ID:
        case INT:
        case FLOAT:
        case BOOLEAN:
            StmtList->stmt = stmt();
            StmtList->nextStmt = stmtList(NextStmt);
            return StmtList;
    }
    return NULL;
}

node_Stmt* stmt(){
/*
    --> if ( <Expr> ) <Stmt1> <If2>		|
						while ( <Expr> ) <Stmt1>		|
						System.<PrintRead>;				|
						return <Expr>;					|
						<Stmt2>
*/

/*
    fprintf(stdout, "<Stmt>\n");
*/

    node_If *StmtIf;
    node_While *StmtW;
    node_Stmt *Stmt;
    node_Return *StmtR;

    switch(lookAhead){
        case IF:
            eat(IF);
            StmtIf = new node_If(NULL, NULL, NULL, 0);
            StmtIf->line = getLine();
            eatOrSkipTo(LPARENT, stmt_sync1);
            StmtIf->condition = expr();
            eatOrSkipTo(RPARENT, stmt_sync2);
            StmtIf->stmtList = stmt1();
            StmtIf->elseStmtList = if2();
            return StmtIf;

        case WHILE:
            eat(WHILE);
            StmtW = new node_While(NULL, NULL, 0);
            StmtW->line = getLine();
            eatOrSkipTo(LPARENT, stmt_sync1);
            StmtW->condition = expr();
            eatOrSkipTo(RPARENT, stmt_sync2);
            StmtW->stmtList = stmt1();
            return StmtW;

        case SYSTEM:
            eat(SYSTEM);
            eatOrSkipTo(DOT, stmt_sync3);
            Stmt = printRead();
            eatOrSkipTo(SEMICOLON, stmt_sync4);
            return Stmt;

        case RETURN:
            eat(RETURN);
            StmtR = new node_Return(NULL, 0);
            StmtR->line = getLine();
            StmtR->exp = expr();
            eatOrSkipTo(SEMICOLON, stmt_sync4);
            return StmtR;

        case ID:
        case THIS:
        case INT:
        case FLOAT:
        case BOOLEAN:
            return stmt2();

        default:
            ErrorMessage(INVALID_STATEMENT, getLine());
            skip(stmt_sync4);
            return NULL;
    }
}

node_Stmt* stmt2(){
/*
    --> ID<DeclORExpr>					|
						this<Name2><IsAssign>			|
						<PrimitiveType>   ID<IdList2>;
*/

/*
    fprintf(stdout, "<Stmt2>\n");
*/

    node_Type *Type = new node_Type(NULL, 0, false);
    node_VarList *varList = new node_VarList(NULL, NULL);
    node_VarList *NextVar = new node_VarList(NULL, NULL);
    node_NameDecl *varDecl = new node_NameDecl(NULL, NULL, 0);
    node_Name *var = new node_Name(NULL, false, false, NULL, NULL, false, NULL, 0);
    var->line = getLine();
    varDecl->line = getLine();
    node_Name *aux;

    switch(lookAhead){
        case ID:
            Type->className = (char*)malloc((getLexemeLength()+1)*sizeof(char));
            strcpy(Type->className, getLexeme());
            var->name = (char*)malloc((getLexemeLength()+1)*sizeof(char));
            strcpy(var->name, getLexeme());
            eat(ID);
            return declORExpr(Type, var);

        case THIS:
            eat(THIS);
            var->isThis = true;
            aux = name2(var);
            return isAssign(aux);

        case INT:
        case FLOAT:
        case BOOLEAN:
            Type->primitiveType = primitiveType();
            Type->isArray = isArray;
            if(lookAhead == ID){
                varDecl->varName = (char*)malloc((getLexemeLength()+1)*sizeof(char));
                strcpy(varDecl->varName, getLexeme());
                eat(ID);
            }else{
                ErrorMessage(getLine(), lookAhead, ID);
                skip(stmt2_sync1);
            }
            varDecl->line = getLine();
            varDecl->varType = Type;
            varList->VarName = varDecl;
            varList->nextDecl = idList2(NextVar, Type);
            eatOrSkipTo(SEMICOLON, stmt2_sync2, SEMICOLON_EXPECTED);
            return new node_VarListStmt(varList);

        default:
            ErrorMessage(getLine(), lookAhead, SEMICOLON_EXPECTED);
            skip(stmt2_sync3);
            return NULL;
    }
}

node_Stmt* declORExpr(node_Type *Type, node_Name *Var){
/*
    --> ID<idList2>;								|
        <Name1><IsAssign>               |
        (<ExprList>)<Name2><IsAssign>
*/

/*
    fprintf(stdout, "<DeclORExpr>\n");
*/

    node_VarListStmt *DeclStmt = new node_VarListStmt(NULL);
    node_Name* aux;

    node_VarList *varList = new node_VarList(NULL, NULL);
    node_VarList *varList2 = new node_VarList(NULL, NULL);
    node_NameDecl *variable = new node_NameDecl(Type, NULL, 0);

    switch(lookAhead){
        case ID:
            variable->varName = (char*)malloc((getLexemeLength()+1)*sizeof(char));
            strcpy(variable->varName, getLexeme());
            variable->line = getLine();
            eat(ID);
            varList->VarName = variable;
            varList->nextDecl = idList2(varList2, Type);
            eatOrSkipTo(SEMICOLON, declORExpr_sync1);
            DeclStmt->varList = varList;
            return DeclStmt;

        case LBRACKET:
        case DOT:
        case SEMICOLON:
        case ASSIGN:
            aux = name1(Var);
            return isAssign(aux);

        case LPARENT:
            eatOrSkipTo(LPARENT, declORExpr_sync2);
            Var->sentParamsExpList = exprList();
            eatOrSkipTo(RPARENT, declORExpr_sync3);
            aux = Var;
            aux = name2(aux);
            return isAssign(aux);

        default:
            ErrorMessage(INVALID_EXPRESSION, getLine());
            skip(declORExpr_sync1);
            return NULL;
    }
}

node_Stmt* isAssign(node_Name *Name){
/*
    --> ;			 					|
						= <Assign>;
*/

/*
    fprintf(stdout, "<IsAssign>\n");
*/

    node_Call *call = new node_Call(NULL, 0);
    node_Assign *Assign = new node_Assign(NULL, NULL, 0);

    switch(lookAhead){
        case SEMICOLON:
            call->line = getLine();
            call->callMethod = Name;
            eat(SEMICOLON);
            return call;

        case ASSIGN:
            Assign->line = getLine();
            eat(ASSIGN);
            Assign->Name = Name;
            Assign->assignmentExp = assign();
            eatOrSkipTo(SEMICOLON, isAssign_sync);
            return Assign;

        default:
            ErrorMessage(INVALID_EXPRESSION, getLine());
            skip(isAssign_sync);
            return NULL;
    }
}

node_StmtList* if2(){
/*
    --> else <Stmt1>					|
						epsilon
*/

/*
    fprintf(stdout, "<If2>\n");
*/

    if(lookAhead == ELSE){
        eat(ELSE);
        return stmt1();
    }
    return NULL;
}

node_Stmt* printRead(){
/*
    -->	out.println( <ExprList> )		|
        in.read( <Expr> )
*/

/*
    fprintf(stdout, "<printRead>\n");
*/

    node_Println *println = new node_Println(NULL, 0);
    node_Read *read = new node_Read(NULL, 0);

    switch(lookAhead){
        case OUT:
        case PRINTLN:
            eatOrSkipTo(OUT, printRead_sync);
            eatOrSkipTo(DOT, printRead_sync);
            eatOrSkipTo(PRINTLN, printRead_sync);
            eatOrSkipTo(LPARENT, printRead_sync);
            println->line = getLine();
            println->expList = exprList();
            eatOrSkipTo(RPARENT, printRead_sync);
            return println;

        case IN:
        case READ:
            eatOrSkipTo(IN, printRead_sync);
            eatOrSkipTo(DOT, printRead_sync);
            eatOrSkipTo(READ, printRead_sync);
            eatOrSkipTo(LPARENT, printRead_sync);
            read->line = getLine();
            read->exp = expr();
            eatOrSkipTo(RPARENT, printRead_sync);
            return read;

        default:
            ErrorMessage(INVALID_STATEMENT, getLine());
            skip(printRead_sync);
            return NULL;
    }
}

node_StmtList* stmt1(){
/*
    --> { <StmtList> }					|
						<Stmt>
*/

/*
    fprintf(stdout, "<Stmt1>\n");
*/

    node_StmtList *StmtList = new node_StmtList(NULL, NULL);
    node_StmtList *StmtList2;

    switch(lookAhead){
        case LBRACE:
            eat(LBRACE);
            StmtList2 = stmtList(StmtList);
            eatOrSkipTo(RBRACE, stmt1_sync, RBRACE_IF_EXPECTED);
            return StmtList2;

        default:
            StmtList->stmt = stmt();
            return StmtList;
    }
}

node_Exp* assign(){
/*
    -->	new <New>						|
						<Expr>
*/

/*
    fprintf(stdout, "<Assign>\n");
*/

    switch(lookAhead){
        case NEW:
            eat(NEW);
            return New();

        case NOT:
        case PLUS:
        case SUB:
        case INTNUM:
        case REALNUM:
        case LITERAL:
        case TRUE:
        case FALSE:
        case LPARENT:
        case ID:
        case THIS:
            return expr();

        default:
            ErrorMessage(INVALID_EXPRESSION, getLine());
            skip(assign_sync);
            return NULL;
    }
}

node_Exp* New(){
/*
    --> int[ <Expr> ]					|
						float[ <Expr> ]					|
						ID()
*/

/*
    fprintf(stdout, "<New>\n");
*/

    node_New *New = new node_New(NULL, NULL, 0);
    New->line = getLine();
    node_Type *Type = new node_Type(NULL, 0, true);

    switch(lookAhead){
        case INT:
            eat(INT);
            Type->primitiveType = INTVECTOR;
            New->type = Type;
            eatOrSkipTo(LBRACKET, New_sync1);
            New->arrayLengthExp = expr();
            eatOrSkipTo(RBRACKET, New_sync2);
            return New;

        case FLOAT:
            eat(FLOAT);
            Type->primitiveType = REALVECTOR;
            New->type = Type;
            eatOrSkipTo(LBRACKET, New_sync1);
            New->arrayLengthExp = expr();
            eatOrSkipTo(RBRACKET, New_sync2);
            return New;

        case ID:
            Type->className = (char*)malloc((getLexemeLength()+1)*sizeof(char));
            strcpy(Type->className, getLexeme());
            eat(ID);
            New->type = Type;
            eatOrSkipTo(LPARENT, New_sync3);
            eatOrSkipTo(RPARENT, New_sync2);
            return New;

        default:
            ErrorMessage(INVALID_INSTANCING, getLine());
            skip(New_sync2);
            return NULL;
    }
}

node_ExpList* exprList(){
/*
    --> <ExprListTail>					|
						epsilon
*/

/*
    fprintf(stdout, "<ExprList>\n");
*/

    switch(lookAhead){
        case NOT:
        case PLUS:
        case SUB:
        case INTNUM:
        case REALNUM:
        case LITERAL:
        case TRUE:
        case FALSE:
        case LPARENT:
        case ID:
        case THIS:
            return exprListTail();

    }
    return NULL;
}

node_ExpList* exprListTail(){
/*
    -->	<Expr> <HaveExprListTail>
*/

/*
    fprintf(stdout, "<ExprListTail>\n");
*/

    node_ExpList *expL = new node_ExpList(NULL,NULL);

    expL->exp = expr();
    expL->nextExp = haveExprListTail();
    return expL;
}

node_ExpList* haveExprListTail(){
/*
        -->	, <ExprListTail>				|
						epsilon
*/

/*
    fprintf(stdout, "<HaveExprListTail>\n");
*/

    if(lookAhead == COMMA){
        eat(COMMA);
        return exprListTail();
    }

    return NULL;
}

node_Name* name(){
/*
    --> ID<Name3> 						|
						this<Name2>
*/

/*
    fprintf(stdout, "<Name>\n");
*/

    node_Name *Name = new node_Name(NULL, false, false, NULL, NULL, false, NULL, 0);
    node_Name *aux;
    Name->line = getLine();


    switch(lookAhead){
        case ID:
            Name->name = (char*)malloc((getLexemeLength()+1)*sizeof(char));
            strcpy(Name->name, getLexeme());
            eat(ID);
            return name3(Name);

        case THIS:
            eat(THIS);
            Name->isThis = true;
            aux = name2(Name);
            return aux;

        default:
            ErrorMessage(INVALID_EXPRESSION, getLine());
            skip(name_sync);
            return NULL;
    }
}

node_Name* name1(node_Name *Name){
/*
    --> [ <Expr> ]<Name2>				|
	<Name2>						|
*/

/*
    fprintf(stdout, "<Name1>\n");
*/

    node_Name *aux;



    switch(lookAhead){
        case LBRACKET:
            eat(LBRACKET);
            Name->isArray = true;
            Name->index = expr();
            eatOrSkipTo(RBRACKET, name1_sync);
            aux = name2(Name);
            return aux;

        default:
            aux = Name;
            aux = name2(aux);
            return aux;
    }
}

node_Name* name2(node_Name* Name){
/*
    --> .<HaveLength>			|
        epsilon
*/

/*
    fprintf(stdout, "<Name2>\n");
*/

    node_Name *aux;

    if(lookAhead == DOT){
        eat(DOT);
        aux = Name;
        aux->nextName = haveLength(aux);
        return aux;
    }
    if(Name->isThis || Name->name) return Name;
    return NULL;
}

node_Name* name3(node_Name *Name){
/*
    --> <Name1>                                 |
        ( <ExprList> ) <Name2>
 */

/*
    fprintf(stdout, "<Name3>\n");
*/

    switch (lookAhead){
        case LPARENT:
            eatOrSkipTo(LPARENT, name3_sync2);
            Name->sentParamsExpList = exprList();
            Name->isMethod = true;
            eatOrSkipTo(RPARENT, name3_sync3);
            return name1(Name);

        default: return name1(Name);
    }
}

node_ExpList* isMethod(){
/*
    --> ( <ExprList> )					|
        epsilon
*/

/*
    fprintf(stdout, "<IsMethod>\n");
*/

    node_ExpList *ExpList;

    switch (lookAhead){
        case LPARENT :            
            eatOrSkipTo(LPARENT, isMethod_sync1);
            ExpList = exprList();
            eatOrSkipTo(RPARENT, isMethod_sync2);
            ismethod = true;
            return ExpList;
    }
    return NULL;
}

node_Exp* expr(){
/*
    --> <OpOR>
*/

/*
    fprintf(stdout, "<Expr>\n");
*/

    return opOR();
}

node_Exp* opOR(){
/*
    --> <opAND> <opOR1>
*/

/*
    fprintf(stdout, "<OpOR>\n");
*/

    return opOR1(opAND());
}

node_Exp* opOR1(node_Exp *exp){
/*
    --> "||" <opAND> <opOR1>    |
	epsilon
*/

/*
    fprintf(stdout, "<opOR1>\n");
*/

    node_RelationalOP *relOp = new node_RelationalOP(0, exp, NULL, 0);
    if(lookAhead == ORLOGIC){
        eat(ORLOGIC);
        relOp->line = getLine();
        relOp->relOp = ORLOGIC;
        relOp->rightExp = opOR1(opAND());
        return (node_Exp*)relOp;
    }
    return exp;
}

node_Exp* opAND(){
/*
    --> <opEq> <opAND1>
*/

/*
    fprintf(stdout, "<opAND>\n");
*/

    return opAND1(opEq());
}

node_Exp* opAND1(node_Exp* exp){
/*
    --> "&&" <opEq> <opAND1>    |
	epsilon
*/
    node_RelationalOP *relOp = new node_RelationalOP(0, exp, NULL, 0);
    relOp->line = getLine();

/*
    fprintf(stdout, "<opAND1>\n");
*/

    if(lookAhead == ANDLOGIC){
        eat(ANDLOGIC);
        relOp->relOp = ANDLOGIC;
        relOp->rightExp = opAND1(opEq());
        return (node_Exp*)relOp;
    }
    return exp;
}

node_Exp* opEq(){
/*
    --> <OpRel> <opEq1>
*/

/*
    fprintf(stdout, "<opEq>\n");
*/

    return opEq1(opRel());
}

node_Exp* opEq1(node_Exp* exp){
/*
    --> "==" <OpRel> <opEq1>	|
	"!=" <OpRel> <opEq1>	|
	epsilon
*/
    node_RelationalOP *relOp = new node_RelationalOP(0, exp, NULL, 0);
    relOp->line = getLine();

/*
    fprintf(stdout, "<opEq1>\n");
*/

    switch(lookAhead){
        case EQUAL:
            eat(EQUAL);
            relOp->relOp = EQUAL;
            relOp->rightExp = opEq1(opRel());
            return (node_Exp*)relOp;

        case NOTEQUAL:
            eat(NOTEQUAL);
            relOp->relOp = NOTEQUAL;
            relOp->rightExp = opEq1(opRel());
            return (node_Exp*)relOp;
    }
    return  exp;
}

node_Exp* opRel(){
/*
    --> <OpBinOR> <OpRel1>
*/

/*
    fprintf(stdout, "<OpRel>\n");
*/

    return opRel1(opBinOR());
}

node_Exp* opRel1(node_Exp* exp){
/*
    -->	"<"  <OpBinOR> <OpRel1>	|
	"<=" <OpBinOR> <OpRel1>	|
	">"  <OpBinOR> <OpRel1>	|
	">=" <OpBinOR> <OpRel1>	|
	epsilon
*/

/*
    fprintf(stdout, "<OpRel1>\n");
*/

    node_BooleanOP *boolOp = new node_BooleanOP(0, exp, NULL, 0);
    boolOp->line = getLine();
    switch(lookAhead){
        case LESST:
            eat(LESST);
            boolOp->boolOp = LESST;
            boolOp->rightExp = opRel1(opBinOR());
            return (node_Exp*)boolOp;

        case LESSEQ:
            eat(LESSEQ);
            boolOp->boolOp = LESSEQ;
            boolOp->rightExp = opRel1(opBinOR());
            return (node_Exp*)boolOp;

        case GREATERT:
            eat(GREATERT);
            boolOp->boolOp = GREATERT;
            boolOp->rightExp = opRel1(opBinOR());
            return (node_Exp*)boolOp;

        case GREATEREQ:
            eat(GREATEREQ);
            boolOp->boolOp = GREATEREQ;
            boolOp->rightExp = opRel1(opBinOR());
            return (node_Exp*)boolOp;
    }
    return exp;
}

node_Exp* opBinOR(){
/*
    --> <OpBinAND> <OpBinOR1>
*/

/*
    fprintf(stdout, "<OpBinOR>\n");
*/

    return opBinOR1(opBinAND());
}

node_Exp* opBinOR1(node_Exp* exp){
/*
    -->	"+" <OpBinAND> <OpBinOR1>   |
	"-" <OpBinAND> <OpBinOR1>   |
	"|" <OpBinAND> <OpBinOR1>   |
	epsilon
*/

/*
    fprintf(stdout, "<OpBinOR1>\n");
*/

    node_BinOp *binOp = new node_BinOp(0, exp, NULL, 0);
    binOp->line = getLine();
    node_BitwiseOP *bitOp = new node_BitwiseOP(0, exp, NULL, 0);
    bitOp->line = getLine();
    switch(lookAhead){
        case PLUS:
            eat(PLUS);
            binOp->binOp = PLUS;
            binOp->rightExp = opBinOR1(opBinAND());
            return (node_Exp*)binOp;

        case SUB:
            eat(SUB);
            binOp->binOp = SUB;
            binOp->rightExp = opBinOR1(opBinAND());
            return (node_Exp*)binOp;

        case ORBIT:
            eat(ORBIT);
            bitOp->bitOp = ORBIT;
            bitOp->rightExp = opBinOR1(opBinAND());
            return (node_Exp*)bitOp;
    }
    return exp;
}

node_Exp* opBinAND(){
/*
    --> <UnOp> <OpBinAND1>
*/

/*
    fprintf(stdout, "<OpBinAND>\n");
*/

    return opBinAND1(unOp());
}

node_Exp* opBinAND1(node_Exp *exp){
/*
    -->	"*" <UnOp> <OpBinAND1>	|
	"/" <UnOp> <OpBinAND1>	|
	"%" <UnOp> <OpBinAND1>	|
	"&" <UnOp> <OpBinAND1>	|
	epsilon
*/

/*
    fprintf(stdout, "<OpBinAND1>\n");
*/

    node_Bin2Op *bin2Op = new node_Bin2Op(0, exp, NULL, 0);
    bin2Op->line = getLine();
    node_BitwiseOP *bitOp = new node_BitwiseOP(0, exp, NULL, 0);
    bitOp->line = getLine();

    switch(lookAhead){
        case MULT:
            eat(MULT);
            bin2Op->bin2Op = MULT;
            bin2Op->rightExp = opBinAND1(unOp());
            return (node_Exp*)bin2Op;

        case DIV:
            eat(DIV);
            bin2Op->bin2Op = DIV;
            bin2Op->rightExp = opBinAND1(unOp());
            return (node_Exp*)bin2Op;

        case MOD:
            eat(MOD);
            bin2Op->bin2Op = MOD;
            bin2Op->rightExp = opBinAND1(unOp());
            return (node_Exp*)bin2Op;

        case ANDBIT:
            eat(ANDBIT);
            bitOp->bitOp = ANDBIT;
            bitOp->rightExp = opBinAND1(unOp());
            return (node_Exp*)bitOp;
    }
    return exp;
}

node_Exp* unOp(){
/*
    --> "!" <UnOp>		|
	"+" <UnOp>		|
	"-" <UnOp> 		|
	<VarConst>
*/

/*
    fprintf(stdout, "<UnOp>\n");
*/

    node_Sign *sign = new node_Sign(0, NULL, 0);
    sign->line = getLine();
    node_Not *nodeNot = new node_Not(NULL, 0);
    nodeNot->line = getLine();
    switch(lookAhead){
        case NOT:
            eat(NOT);
            nodeNot->exp = unOp();
            return (node_Exp*)nodeNot;

        case PLUS:
            eat(PLUS);
            sign->sign = PLUS;
            sign->exp = unOp();
            return (node_Exp*)sign;

        case SUB:
            eat(SUB);
            sign->sign = SUB;
            sign->exp = unOp();
            return (node_Exp*)sign;

        case INTNUM:
        case REALNUM:
        case LITERAL:
        case TRUE:
        case FALSE:
        case ID:
        case THIS:
        case LPARENT:
            return varConst();

        default:
            ErrorMessage(INVALID_EXPRESSION, getLine());
            skip(unOp_sync);
            return NULL;
    }
}

node_Exp* varConst(){
/*
    --> NUM                     |
	LITERAL			|
	true			|
	false			|
        ( <Expr> )              |
	<Name>
*/

/*
    fprintf(stdout, "<VarConst>\n");
*/

    node_Number *number = new node_Number(0, NULL, 0);
    number->line = getLine();
    node_Literal *literal = new node_Literal(NULL, 0);
    literal->line = getLine();
    node_Logic *nL = new node_Logic(0, 0);
    nL->line = getLine();

    switch(lookAhead){
        case INTNUM:
            number->number = (char*)malloc((getLexemeLength()+1)*sizeof(char));
            strcpy(number->number, getLexeme());
            eat(INTNUM);
            number->type = INTNUM;
            return (node_Exp*)number;

        case REALNUM:
            number->number = (char*)malloc((getLexemeLength()+1)*sizeof(char));
            strcpy(number->number, getLexeme());
            eat(REALNUM);
            number->type = REALNUM;
            return (node_Exp*)number;

        case LITERAL:
            literal->literal = (char*)malloc((getLexemeLength()+1)*sizeof(char));
            strcpy(literal->literal, getLexeme());
            eat(LITERAL);
            return (node_Exp*)literal;

        case TRUE:
            eat(TRUE);
            nL->logic = TRUE;
            return (node_Exp*)nL;

        case FALSE:
            eat(FALSE);
            nL->logic = FALSE;
            return (node_Exp*)nL;

        case LPARENT:
            eat(LPARENT);
            node_Exp *expParent;
            expParent = expr();
            eatOrSkipTo(RPARENT, varConst_sync);
            return expParent;

        case THIS:
        case ID:
            return (node_Exp*)new node_NameExp(name());

        default:
            ErrorMessage(INVALID_EXPRESSION, getLine());
            skip(varConst_sync);
            return NULL;
    }
}

node_Name* haveLength(node_Name* Name){
/*
    --> length  						|
        ID<IsMethod><Name1>
*/

/*
    fprintf(stdout, "<HaveLength>\n");
*/

    node_Name *NextName = new node_Name(NULL, false, false, NULL, NULL, false, NULL, 0);
    NextName->line = getLine();

    switch (lookAhead) {
        case LENGTH:
            eat(LENGTH);
            Name->length = true;
            return NULL;

        case ID:
            NextName->name = (char*)malloc((getLexemeLength()+1)*sizeof(char));
            strcpy(NextName->name, getLexeme());
            eat(ID);
            NextName->sentParamsExpList = isMethod();
            if(ismethod) NextName->isMethod = true;
            ismethod = false;
            return name1(NextName);

        default:
            ErrorMessage(INVALID_EXPRESSION, getLine());
            skip(haveLength_sync);
            return NULL;
    }
}
